﻿// Decompiled with JetBrains decompiler
// Type: Netick.NetworkHashSet`1
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

#nullable disable
namespace Netick;

/// <summary>
/// A networked <see cref="T:System.Collections.Generic.HashSet`1" /> collection.
/// </summary>
public sealed class NetworkHashSet<T> : 
  NetworkHashSet,
  INetworkCollection,
  IEnumerable<T>,
  IEnumerable
  where T : unmanaged
{
  internal int _offsetToIterationList;
  private HashSet<T> _original;

  public NetworkHashSet(int capacity)
  {
    this._length = capacity;
    this._lengthDoubled = capacity * 2;
    this._original = new HashSet<T>(capacity);
  }

  public override unsafe void InternalInit(
    INetickNetworkScript beh,
    int* state,
    int elementSizeWords,
    int flags)
  {
    base.InternalInit(beh, state, elementSizeWords, flags);
    this._entrySizeWords = NetickUtils.GetWordSize(sizeof (NetworkHashSet<T>.Entry));
    this._offsetToIterationList = 1 + this._lengthDoubled * this._entrySizeWords;
  }

  public override unsafe void InternalReset()
  {
    *this._intS = 0;
    for (int index = 0; index < this._length; ++index)
      *this.GetIterationListElementPointer(index) = 0;
    for (int index = 0; index < this._lengthDoubled; ++index)
    {
      this.GetEntryElementPointer(index)->IterationIndex = -1;
      this.GetEntryElementPointer(index)->Key = default (T);
    }
    foreach (T obj in this._original)
      this.Add(obj);
    this.Count = this._original.Count;
  }

  /// <summary>Gets the number of elements contained in the set.</summary>
  public unsafe int Count
  {
    get => (IntPtr) this._intS == IntPtr.Zero ? this._original.Count : *this._intS;
    internal set
    {
      this._entity.SetState_Collection(&value, this._intS, 1, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
    }
  }

  private unsafe NetworkHashSet<T>.Entry* GetEntryElementPointer(int index)
  {
    return (NetworkHashSet<T>.Entry*) (this._intS + 1 + index * this._entrySizeWords);
  }

  private unsafe NetworkHashSet<T>.Entry GetEntryElementData(int index)
  {
    return *(NetworkHashSet<T>.Entry*) (this._intS + 1 + index * this._entrySizeWords);
  }

  private unsafe int* GetIterationListElementPointer(int index)
  {
    return this._intS + this._offsetToIterationList + index;
  }

  private unsafe int GetIterationListElementData(int index)
  {
    return (this._intS + this._offsetToIterationList)[index];
  }

  internal unsafe void SetEntryElementData(int index, NetworkHashSet<T>.Entry value)
  {
    this._entity.SetState_Collection((int*) &value, this._intS + 1 + index * this._entrySizeWords, this._entrySizeWords, 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
  }

  internal unsafe void SetIterationListElement(int index, int value)
  {
    this._entity.SetState_Collection(&value, this._intS + this._offsetToIterationList + index, 1, 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
  }

  /// <summary>Adds an item to the set.</summary>
  /// <param name="item">The element to add.</param>
  /// <returns>true if the element is added to the set; false if the element is already present or the set was full.</returns>
  public unsafe bool Add(T item)
  {
    if ((IntPtr) this._intS == IntPtr.Zero)
    {
      this._original.Add(item);
      return true;
    }
    if (this.Count >= this._length)
      return false;
    int index = (item.GetHashCode() & int.MaxValue) % this._lengthDoubled;
    int num1 = index;
    int num2 = -1;
    do
    {
      NetworkHashSet<T>.Entry entryElementData = this.GetEntryElementData(index);
      if (num2 == -1)
      {
        if (entryElementData.IsEmpty)
          num2 = index;
        else if (EqualityComparer<T>.Default.Equals(entryElementData.Key, item))
          return false;
        index = (index + 1) % this._lengthDoubled;
      }
      else
        break;
    }
    while (index != num1);
    if (num2 == -1)
      return false;
    this.SetEntryElementData(index, new NetworkHashSet<T>.Entry(item, this.Count, true));
    this.SetIterationListElement(this.Count, num2);
    ++this.Count;
    return true;
  }

  /// <summary>Removes the specified item from the set.</summary>
  /// <param name="item">The element to remove.</param>
  /// <returns>true if the element is successfully removed; otherwise, false.</returns>
  public bool Remove(T item)
  {
    NetworkHashSet<T>.Entry entry1;
    int entry2 = this.FindEntry(item, out entry1);
    if (entry2 == -1)
      return false;
    int iterationIndex = entry1.IterationIndex;
    int iterationListElementData = this.GetIterationListElementData(this.Count - 1);
    NetworkHashSet<T>.Entry entryElementData1 = this.GetEntryElementData(iterationListElementData) with
    {
      IterationIndex = iterationIndex
    };
    this.SetEntryElementData(iterationListElementData, entryElementData1);
    this.SetIterationListElement(iterationIndex, this.GetIterationListElementData(this.Count - 1));
    NetworkHashSet<T>.Entry entryElementData2 = this.GetEntryElementData(entry2) with
    {
      IterationIndex = -1
    };
    this.SetEntryElementData(entry2, entryElementData2);
    --this.Count;
    return true;
  }

  /// <summary>Determines whether the set contains a specific value.</summary>
  /// <param name="item">The object to locate in the set.</param>
  /// <returns>true if item is found; otherwise, false.</returns>
  public bool Contains(T item) => this.FindEntry(item, out NetworkHashSet<T>.Entry _) != -1;

  private int FindEntry(T key, out NetworkHashSet<T>.Entry value)
  {
    int index = (key.GetHashCode() & int.MaxValue) % this._lengthDoubled;
    int num = index;
    do
    {
      NetworkHashSet<T>.Entry entryElementData = this.GetEntryElementData(index);
      if (entryElementData.IsEverUsed == 0)
      {
        value = new NetworkHashSet<T>.Entry();
        return -1;
      }
      if (!entryElementData.IsEmpty && EqualityComparer<T>.Default.Equals(entryElementData.Key, key))
      {
        value = entryElementData;
        return index;
      }
      index = (index + 1) % this._lengthDoubled;
    }
    while (index != num);
    value = new NetworkHashSet<T>.Entry();
    return -1;
  }

  /// <summary>Removes all item from the set.</summary>
  public void Clear()
  {
    for (int index = 0; index < this._lengthDoubled; ++index)
      this.SetEntryElementData(index, new NetworkHashSet<T>.Entry(default (T), -1, false));
    this.Count = 0;
  }

  public bool Overlaps(IEnumerable<T> other)
  {
    foreach (T obj in other)
    {
      if (this.Contains(obj))
        return true;
    }
    return false;
  }

  public void UnionWith(IEnumerable<T> other)
  {
    foreach (T obj in other)
      this.Add(obj);
  }

  public unsafe void IntersectWith(IEnumerable<T> other)
  {
    int count = this.Count;
    // ISSUE: untyped stack allocation
    Span<T> span1 = new Span<T>((void*) __untypedstackalloc(checked (unchecked ((IntPtr) (uint) count) * sizeof (T))), count);
    int num = 0;
    foreach (T obj in this)
    {
      span1[num] = obj;
      ++num;
    }
    Span<T> span2 = span1;
    for (int index = 0; index < span2.Length; ++index)
    {
      T obj = span2[index];
      if (!other.Contains<T>(obj))
        this.Remove(obj);
    }
  }

  public void ExceptWith(IEnumerable<T> other)
  {
    foreach (T obj in other)
      this.Remove(obj);
  }

  public void SymmetricExceptWith(IEnumerable<T> other)
  {
    foreach (T obj in other)
    {
      if (this.Contains(obj))
        this.Remove(obj);
      else
        this.Add(obj);
    }
  }

  public IEnumerator<T> GetEnumerator()
  {
    for (int i = 0; i < this.Count; ++i)
      yield return this.GetEntryElementData(this.GetIterationListElementData(i)).Key;
  }

  IEnumerator IEnumerable.GetEnumerator() => (IEnumerator) this.GetEnumerator();

  [StructLayout(LayoutKind.Sequential, Pack = 4)]
  internal struct Entry(T key, int iterationIndex, bool isEverUsed)
  {
    public int IterationIndex = iterationIndex;
    public int IsEverUsed = isEverUsed ? 1 : 0;
    public T Key = key;

    public bool IsEmpty => this.IterationIndex == -1;
  }
}
